/**
 * <html>
 *   <body>
 *     <p>Copyright(C)版权所有 - 2016 广州云医科技有限公司.</p>
 *     <p>All rights reserved.</p>
 *     <p>Created on 2015年3月16日</p>
 *   </body>
 * </html>
 */
package cn.yunyichina.provider.pay.wechat.sdk;

import cn.yunyichina.provider.framework.dubbo.service.CacheService;
import cn.yunyichina.provider.framework.exception.CallException;
import cn.yunyichina.provider.iface.entity.ErrorCodeDefine;
import cn.yunyichina.provider.pay.wechat.utils.http.HttpClient;
import cn.yunyichina.provider.pay.wechat.utils.http.HttpResponse;
import cn.yunyichina.utils.convert.JsonUtils;
import cn.yunyichina.utils.network.HttpsUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * 微信SDK
 */
public class WechatSDK {

	private static Logger logger = LoggerFactory.getLogger(WechatSDK.class);

	/**
	 * 存储 Wechat Access token
	 */
	public static Map<String, JSONObject> wechatAccessTokenMap = new HashMap<String, JSONObject>();

	/** 微信网关 */
	public static final String WECHAT_GATEWAY="https://api.weixin.qq.com";

	public static HttpClient httpClient = new HttpClient(10, 20000, 20000, false);
    
    public static final String CACHE_KEY_NOT_EXIST = "nil";
    /**
	 * 消息发送令牌默认过期时间，可能变化
	 */
	private static int accessTokenTimeOut = 1800;  //实际为 7200
	/**
	 * 上次检查access_token是否过期的时间点
	 */
	private static long lastCheckAccessTokenTime = System.currentTimeMillis() - 600*1000;
	
	/**
	 * 微信小程序消息发送令牌请求URL
	 */
	private static final String ACCESS_TOKEN_REQUEST_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
	
	
	
	/**
	 * 获取消息发送令牌
	 * @param appId
	 * @param appSecret
	 * @return 如果无法获取消息发送令牌，抛出异常
	 */
	public static String getAccessToken(String appId,String appSecret)  {
		logger.info("getAccessToken:appId="+appId+",appSecret="+appSecret);
		
		//检查是否需要重新获取
		checkAccessTokenTimeOut(appId, appSecret);
		
		String accessToken = CacheService.getGlobalKey("message", "small_program", "access_tocken_"+appId);
		logger.info("getAccessToken-->accessToken="+accessToken);
		if (isValidCacheValue(accessToken)) {//有效令牌
			return accessToken;
		}
		else {//无效令牌
			logger.info("无效的消息发送令牌："+accessToken);
			throw new CallException(ErrorCodeDefine.MESSAGE_ACCESS_TOKEN, "无法获取消息发送令牌：appId="+appId+",appSecret="+appSecret);
		}
		
	}
	
	/**
	 * 删除微信发送令牌
	 * @param appId
	 */
	public static void deleteAccessToken(String appId) {
		lastCheckAccessTokenTime = 1000;//重置检查时间
		CacheService.delGlobalKey("message", "small_program", "access_tocken_" + appId);
	}
	
	/**
	 * 检查消息发送令牌是否需要重新获取，如则进行获取令牌，并存入缓存。
	 * @param appId
	 * @param appSecret
	 */
	private static void checkAccessTokenTimeOut(String appId,String appSecret) {

		String accessToken = CacheService.getGlobalKey("message", "small_program", "access_tocken_"+appId);

		if(StringUtils.isBlank(accessToken)){
			logger.info("checkAccessTokenTimeOut:appId=" + appId + ",appSecret=" + appSecret);

			String url = ACCESS_TOKEN_REQUEST_URL + "&appid=" + appId + "&secret=" + appSecret;
			try {
				String strJsonReturn = HttpsUtil.doSslGet(url, "UTF-8");
				JSONObject response = JsonUtils.parseObject(strJsonReturn);
				if (response.containsKey("errcode")) {// 失败
					logger.error("获取ccess_token失败:" + strJsonReturn);
					throw new CallException(ErrorCodeDefine.MESSAGE_ACCESS_TOKEN,"获取新access_token失败:" + strJsonReturn);
				} else {// 成功
					logger.info("获取access_token成功:" + strJsonReturn);
					// 存入缓存
					CacheService.setGlobalKey("message", "small_program", "access_tocken_" + appId,response.getString("access_token"), accessTokenTimeOut);
				}
			} catch (Exception e) {
				logger.error("获取新access_token失败" , e);
			}
		}
	}
	
	/**
	 * 检查从cache里获取的字符串值是否有效
	 * @param strJson 从cache里获取的字符串值
	 */
	private static boolean isValidCacheValue(String value) {
		if (StringUtils.isNotEmpty(value) && ! value.equalsIgnoreCase(CACHE_KEY_NOT_EXIST)) {
			return true;
		}
		return false;
	}
	/**
	 * 去内存获取AccessToken，有超时机制</br>
	 * 废弃
	 * @deprecated
	 * @param appId
	 * @param appSecret
	 * @return AccessToken
	 */
	public static String getAccessTokenLocal(String appId, String appSecret) {
		String accessToken = "";
		JSONObject at = wechatAccessTokenMap.get(appId);
		if (at != null) {
			// 判断超时否
			long get_time = at.getLongValue("get_time");
			long expires_in = 1000L * ( at.getLongValue("expires_in") - 1000l );
			if ( ( System.currentTimeMillis() - get_time ) > expires_in) {
				// 超时 - 重拿
				accessToken = getAccessTokenByWechat(appId, appSecret);
			} else {
				// 没超时
				accessToken = at.getString("access_token");
			}
		} else {
			accessToken = getAccessTokenByWechat(appId, appSecret);
		}
		return accessToken;
	}


	/**
	 * 去微信服务器获取AccessToken
	 * 
	 * @param appId
	 * @param appSecret
	 * @return AccessToken
	 */
	public static String getAccessTokenByWechat(String appId, String appSecret) {
		String accessToken = "";

		// {"errcode":40013,"errmsg":"invalid appid"}
		String url = WECHAT_GATEWAY + "/cgi-bin/token?grant_type=client_credential";
		url += "&appid=" + appId;
		url += "&secret=" + appSecret;
		logger.info("getAccessTokenByWechat.url:" + url);
		HttpResponse rs = httpClient.get(url);
		if (rs != null && rs.getStatusCode() == 200) {
			JSONObject jo = JSONObject.parseObject(rs.getResponseAsString());
			if (jo.containsKey("access_token")) {
				accessToken = jo.getString("access_token");
				jo.put("get_time", System.currentTimeMillis() + "");

				wechatAccessTokenMap.put(appId, jo);
			} else {
				System.err.println("appid:" + appId + " appSecret:" + appSecret);
				System.err.println(rs.getStatusCode() + " - " + rs.getResponseAsString());
				logger.error("{} - {}", rs.getStatusCode(), rs.getResponseAsString());
			}
		} else {
			logger.debug(rs.toString());
		}
		logger.debug("reget wx access token : {}", accessToken);
		return accessToken;
	}

}
